<?php

namespace plugin\coleopenapi\app\api\controller;

use plugin\coleopenapi\app\logic\ApiLogic;
use plugin\coleopenapi\app\logic\AppSignLogic;
use plugin\coleopenapi\exception\OpenApiException;
use plugin\coleopenapi\limiter\RateLimiter;
use support\Request;
use Webman\Http\Response;
use Wengg\WebmanApiSign\ApiSignService;

class DispatchController
{
    /**
     * @var ApiLogic
     */
    private ApiLogic $apiLogic;
    /**
     * @var AppSignLogic
     */
    private AppSignLogic $appSignLogic;
    /**
     * @var mixed
     */
    private $appInfo;
    /**
     * @var mixed
     */
    private $apiInfo;
    /**
     * @var mixed
     */
    private $method;
    /**
     * @var mixed
     */
    private $appKey;

    public function __construct()
    {
        $this->apiLogic = new ApiLogic();
        $this->appSignLogic = new AppSignLogic();
    }

    /**
     * 路由控制器
     * 处理根据method找到真实路由,并且处理限流
     * @param Request $request
     * @return false|mixed
     * @throws OpenApiException
     */
    public function dispatch(Request $request)
    {

        $service = new ApiSignService;
        $config = $service->getConfig();
        $fields = $config['fields'];

        $this->method = $method = request()->header("method", request()->input("method"));
        $this->appKey = $appKey = request()->header($fields['app_key'], request()->input($fields['app_key']));
        if (!$method) {
            throw new OpenApiException("method参数错误", OpenApiException::METHOD_PARAM_ERROR);
        }

        $this->appInfo = $this->appSignLogic->getInfoByAppKey($appKey);
        if (!$this->appInfo) {
            throw new OpenApiException("当前应用未授权:" . $appKey, OpenApiException::APP_NOT_AUTH_ERROR);
        }
        $this->checkApp($this->appInfo);

        //白名单验证
        if (isset($this->appInfo['white_ip_list'])
            && $this->appInfo['white_ip_list']
            && in_array($request->getRealIp(), explode(',', $this->appInfo['white_ip_list']))) {
            throw new OpenApiException("ip not in white:" . $request->getRealIp(), OpenApiException::IP_NOT_WHITE_ERROR);
        }

        $this->apiInfo = $this->apiLogic->getApiByMethod($method);
        if (!$this->apiInfo) {
            throw new OpenApiException("当前接口不存在:" . $method, OpenApiException::METHOD_NOT_FOUND_ERROR);
        }
        //根据method找到真实的接口控制器和方法
        [$controller, $action] = $this->findAction($method);
        //处理接口限流
        if ($result = $this->limit()) {
            return $result;
        }
        $request->controller = $controller;
        $request->action = $action;
        //执行实际的控制器和方法
        return call_user_func([new $controller, $action], $request);
    }

    public function findAction($method)
    {
        //判断当前appKey对接口是否有权限
        if (!in_array($this->apiInfo['module'], explode(',', $this->appInfo['module']))) {
            throw new OpenApiException("当前接口未授权:" . $method, OpenApiException::API_NOT_AUTH_ERROR);
        }

        $class = $this->apiInfo['target'];
        $clazz = explode('@', $class);
        if (!str_contains($class, '\\')) {
            $controller = "plugin\\coleopenapi\\app\\api\\controller\\" . $clazz[0];
        } else {
            $controller = $clazz[0];
        }
        if (!class_exists($controller)) {
            throw new OpenApiException("当前接口不存在:" . $method, OpenApiException::PARSER_CONTROLLER_ERROR);
        }
        $action = $clazz[1] ?? "index";
        if (!method_exists($controller, $action)) {
            throw new OpenApiException("当前接口不存在:" . $method, OpenApiException::ACTION_NOT_FOUND_ERROR);
        }
        return [$controller, $action];
    }

    public function limiterBody($limit, $window_time)
    {
        return [
            'code' => OpenApiException::RATE_LIMIT_ERROR,
            'message' => '触发限流',
            'data' => [
                'limit' => $limit,
                'window_time' => $window_time,
            ]
        ];
    }

    public function limit()
    {
        //默认限流
        $limit = 200;
        $window_time = 60;
        if ($this->appInfo['is_limit'] == 1) {
            //appKey限流
            $limit = $this->appInfo['limit'];
            $window_time = $this->appInfo['window_time'];
        } else if ($this->apiInfo['is_limit'] == 1) {
            //接口独立配置限流
            $limit = $this->apiInfo['limit'];
            $window_time = $this->apiInfo['window_time'];
        }
        $config = [
            'limit' => $limit, // 请求次数
            'window_time' => $window_time, // 窗口时间，单位：秒
            'limit_key' => $this->appKey . ":" . $this->method, //限流key
            'status' => 429,  // HTTP 状态码
            'body' => $this->limiterBody($limit, $window_time)
        ];
        if ($result = RateLimiter::traffic($config)) {
            return new Response($result['status'], [
                'Content-Type' => 'application/json',
                'X-Rate-Limit-Limit' => $result['limit'],
                'X-Rate-Limit-Remaining' => $result['remaining'],
                'X-Rate-Limit-Reset' => $result['reset']
            ], json_encode($result['body']));
        }
        return $result;
    }

    private function checkApp($appInfo)
    {
        if ($appInfo['status'] == 0) {
            throw new OpenApiException("当前应用已被禁用", OpenApiException::APP_DISABLE_ERROR);
        }
        if (strtotime($appInfo['expired_at']) < time()) {
            throw new OpenApiException("当前应用已过期", OpenApiException::APP_EXPIRED_ERROR);
        }
    }

}